use anyhow::Result;
use dotenv;
use sqlx::{mysql::MySqlConnectOptions, ConnectOptions, MySqlPool, MySql, Pool};

use std::fs::File;
use std::io::Read;
use std::path::PathBuf;
use std::str::FromStr;

pub async fn parse_db() -> Result<Pool<MySql>> {
    let db_url = dotenv::var("FIL_DATABASE_URL");
    if let Err(err) = db_url {
        log::error!("env 'FIL_DATABASE_URL' for database url not found.");
        return Err(anyhow::Error::from(err));
    }
    let db_url = db_url.unwrap();
    let vec: Vec<&str> = db_url.split("@").collect();
    let login = vec[0];
    let left = vec[1];

    let login: Vec<&str> = login.split(":").collect();
    let username = login[0];
    let password = login[1];

    let server = find_string(left, '(', ')');
    let db_name = find_string(left, '/', '?');

    let server: Vec<&str> = server.split(":").collect();
    let db_host = server[0];
    let db_port = server[1];
    let db_port = u16::from_str(db_port).unwrap();
    let db = format!(
        "mysql://{}:{}@{}:{}/{}",
        username, password, db_host, db_port, db_name
    );
    log::info!("database: {}", db);
    let mut options = MySqlConnectOptions::from_str(&db)?;
    options.disable_statement_logging();
    let pool = MySqlPool::connect_with(options).await?;

    Ok(pool)
}

pub fn parse_miner() -> Result<(String, String)> {
    let mut store_home = dotenv::var("LOTUS_MINER_PATH");
    if store_home.is_err() {
        store_home = dotenv::var("LOTUS_STORAGE_PATH");
    }
    if let Ok(p) = store_home {
        return read_path(p)
    }
    log::warn!(
        "both env 'LOTUS_MINER_PATH' and 'LOTUS_MINER_PATH' not found. can't get miner repo."
    );
    let api = dotenv::var("MINER_API_INFO");
    match api {
        Err(e) => Err(anyhow::Error::from(e)),
        Ok(api) => parse_api(api),
    }
}

#[allow(dead_code)]
pub fn parse_lotus() -> Result<(String, String)> {
    let mut store_home = dotenv::var("LOTUS_PATH");
    if store_home.is_err() {
        store_home = dotenv::var("LOTUS_STORAGE_PATH");
    }
    if let Ok(p) = store_home {
        return read_path(p)
    }
    log::warn!(
        "both env 'LOTUS_MINER_PATH' and 'LOTUS_MINER_PATH' not found. can't get miner repo."
    );
    let api = dotenv::var("FULLNODE_API_INFO");
    match api {
        Err(e) => Err(anyhow::Error::from(e)),
        Ok(api) => parse_api(api),
    }
}

fn read_path(input: String) -> Result<(String, String)>{
    log::debug!("store home {}", input);
    let store_home = PathBuf::from(input);
    let api_path = store_home.join("api");
    let api = read_file_to_string(api_path)?;
    let token_path = store_home.join("token");
    let token = read_file_to_string(token_path)?;

    let api_info: Vec<&str> = api.split("/").collect();
    if api_info.len() != 6 {
        return Err(anyhow::Error::msg("api format not right."));
    };

    let api_ip = api_info[2];
    let api_port = api_info[4];
    let miner_url = format!("http://{}:{}/rpc/v0", api_ip, api_port);

    log::info!("token: {}", token);
    log::info!("api: {}", miner_url);
    Ok((token, miner_url))
}

fn parse_api(input: String) -> Result<(String, String)> {
    log::debug!("api {}", input);

    let vec: Vec<&str> = input.split(":").collect();
    let api = vec[1].to_owned();
    let token = vec[0].to_owned();

    let api_info: Vec<&str> = api.split("/").collect();
    if api_info.len() != 6 {
        return Err(anyhow::Error::msg("api format not right."));
    };

    let api_ip = api_info[2];
    let api_port = api_info[4];
    let miner_url = format!("http://{}:{}/rpc/v0", api_ip, api_port);

    log::info!("token: {}", token);
    log::info!("api: {}", miner_url);
    Ok((token, miner_url))
}

fn read_file_to_string(path: PathBuf) -> Result<String> {
    let mut file = File::open(path)?;
    let mut buf = String::new();
    let _ = file.read_to_string(&mut buf)?;
    Ok(buf)
}

fn find_string(input: &str, start: char, stop: char) -> String {
    let mut out = String::new();
    let mut find = false;
    for c in input.chars() {
        if c == start {
            find = true;
            continue;
        }
        if !find {
            continue;
        }
        if c == stop {
            break;
        } else {
            out.push(c);
        }
    }
    out
}